﻿using System;
using System.Collections.Generic;
using System.Text;
using Xeml.Sampling.Contracts;
using Mpi.Common.Collections;
using Xeml.Document;
using System.Diagnostics;
using System.Linq;
using Xeml.Document.Settings;
using Xeml.Document.CoreObjects;

namespace Xeml.SQLDataProviders
{
    /// <summary>
    /// Manages all the (configured) DataProviders
    /// </summary>
    public class DataProviderManager
    {
        #region Singleton
        static readonly DataProviderManager instance = new DataProviderManager();

        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static DataProviderManager()
        {
        }

        DataProviderManager()
        {
            providerComponents = new Dictionary<string, IDataProvider>();
            providers = new Dictionary<string, string>();
            StatusUpdate("Before init");
            Debug.WriteLine("DataProviderManager:\t before init");
            Init();
        }

        public static DataProviderManager Instance
        {
            get
            {
                return instance;
            }
        }
        //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        #endregion

        #region status strip
        public delegate void MessageEmittedEventHandler(object sender, MessageEventArgs e);
        public event MessageEmittedEventHandler MessageEmitted;
 
        public void StatusUpdate(string message)
        {
            if (MessageEmitted != null)
            {
                MessageEmitted(this, new MessageEventArgs(message, EventLogEntryType.Information));
            }
        }
        #endregion

        #region driver section
        private Dictionary<string, IDataProvider> providerComponents;
        private Dictionary<string, string> providers; // classname: DataProvider Uri

        /// <summary>
        /// A collection of available parameter resource components.
        /// key: uri of the data provider
        /// value: the data provider
        /// </summary>
        public Dictionary<string, IDataProvider> GetUriDataProviderDict
        {
            get
            {
                return this.providerComponents;
            }
        }

        /// <summary>
        /// A collection of available IDataProviders. Almost the reverse dictionary of GetUriDataProviderDict
        /// key: the dataprovider name
        /// value: uri of the dataprovider
        /// </summary>
        public Dictionary<string, string> GetNameUriDict
        {
            get
            {
                return providerComponents.ToDictionary(x => x.Value.ComponentName, x => x.Key);
            }
        }

        /// <summary>
        /// Initialize the manager. All available components will be loaded and component information will be read.
        /// </summary>
        private void Init()
        {
            Type t = typeof(IDataProvider);
            IEnumerable<object> dataProviders = AppDomain.CurrentDomain.GetAssemblies().
                SelectMany(x => x.GetTypes()).
                Where(x => t.IsAssignableFrom(x) && !x.IsInterface).
                Select(x => Activator.CreateInstance(x));

            foreach (object o in dataProviders)
            {
                IDataProvider dataProvider = (IDataProvider)o;
                try
                {
                    if (providerComponents.ContainsKey(dataProvider.Uri))
                    {
                        if (providerComponents[dataProvider.Uri].Version <= dataProvider.Version)
                        {
                            providerComponents[dataProvider.Uri] = dataProvider;
                            StatusUpdate(string.Format("provider {0} with higher version replaced", dataProvider.GetType().FullName));
                            Debug.WriteLine(string.Format("provider {0} with higher version replaced", dataProvider.GetType().FullName));
                        }
                    }
                    else
                    {
                        this.providerComponents.Add(dataProvider.Uri, dataProvider);
                        StatusUpdate(string.Format("provider {0} loaded", dataProvider.GetType().FullName));
                        Debug.WriteLine(string.Format("provider {0} loaded", dataProvider.GetType().FullName));
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(string.Format("provider {0} loading failed with exception '{1}'", dataProvider.GetType().FullName, ex.InnerException.GetType().FullName + ": " + ex.InnerException.Message));
                    StatusUpdate(string.Format("provider {0} loading failed with exception '{1}'", dataProvider.GetType().FullName, ex.InnerException.GetType().FullName + ": " +  ex.InnerException.Message));
                }
            }
        }

        /// <summary>
        /// Creates a sample provider.
        /// </summary>
        /// <param name="componentId">The id of the requested component.</param>
        /// <returns>A sample provider instance.</returns>
        /// <exception cref="Xeml.Component.XemlComponentLoadFailed">Will be throwed if the component can'delayTimer be found.</exception>
        public IDataProvider CreateProvider(string uri)
        {
            if (this.Contains(uri))
            {
                IDataProvider ret = null;
                try
                {
                    ret = (IDataProvider)this.providerComponents[uri].Copy();
                }
                catch (Exception ex)
                {
                    throw new XemlComponentLoadFailedException(uri, "Provider exist, but loading failed", ex);
                }

                return ret;
            }
            else
            {
                StatusUpdate(string.Format("unknown provider {0}", uri));
                throw new XemlComponentLoadFailedException(uri, "UnknownSample sample provider.");
            }
        }

        internal bool Contains(string uri)
        {
            return this.providerComponents.ContainsKey(uri);
        }
        #endregion

        #region configured driver section
        public IDataProvider CreateProvider(string providerUri, string configurationName)
        {
            IDataProvider dataProvider = this.CreateProvider(providerUri);
            DataProviderConfiguration config = new DataProviderConfiguration(configurationName);
            dataProvider.Configure(config);

            return dataProvider;
        }
        #endregion
    }
}